#include "primary.h"

#include "zrpc/base/logger.h"
#include "zrpc/base/timer.h"
#include "raft/context.h"
#include "raft/meta.h"
#include "raft/options.h"
#include "raft/raft.h"
#include "raft/raft_log.h"

namespace libraft {
Primary::Primary(zrpc::EventLoop* loop,
                 std::vector<std::unique_ptr<Peer>>& peers,
                 std::shared_ptr<Context> context, Raft* raft,
                 std::shared_ptr<Meta> meta, const Options& options)
    : loop_(loop),
      peers_(peers),
      context_(context),
      raft_(raft),
      meta_(meta),
      options_(options),
      incr_(0) {}

Primary::~Primary() {}

void Primary::NoticeClientTask(TaskType type) {
  // std::unique_lock<std::mutex> lk(raft_->raft_client_mutex_);
  for (const auto& it : peers_) {
    switch (type) {
      case kHeartBeat: {
        it->AddRequestVoteTask();
        break;
      }

      case kNewCommand: {
        it->AddAppendEntriesTask();
        break;
      }
    }
  }
}
void Primary::LaunchHeartBeat() {
  std::unique_lock<std::mutex> lk(context_->global_mutex_);
  if (context_->role == Role::kLeader) {
    NoticeClientTask(kNewCommand);
    AddTask(kHeartBeat);
  }
}

void Primary::LaunchCheckLeader() {
  std::unique_lock<std::mutex> lk(context_->global_mutex_);
  if (context_->role == Role::kFollower || context_->role == Role::kCandidate) {
    if (options_.single_mode) {
      context_->BecomeLeader();
      context_->voted_for_ip = options_.local_ip;
      context_->voted_for_port = options_.local_port;
      meta_->SetCurrentTerm(context_->current_term);
      meta_->SetVotedForIp(context_->voted_for_ip);
      meta_->SetVotedForPort(context_->voted_for_port);
      LOG_INFO << "becomde leader: " << context_->voted_for_ip << " "
               << context_->voted_for_port
               << " current_term: " << context_->current_term;
    } else if (context_->last_op_time + options_.check_leader_us <=
               zrpc::TimeStamp::NowMicros()) {
      context_->BecomeCandidate();
      meta_->SetCurrentTerm(context_->current_term);
      meta_->SetVotedForIp(context_->voted_for_ip);
      meta_->SetVotedForPort(context_->voted_for_port);
      LOG_INFO << "start vote: " << options_.local_ip << " "
               << options_.local_port
               << " current_term: " << context_->current_term;
      NoticeClientTask(kHeartBeat);
    }
  }

  AddTask(kCheckLeader);
}

void Primary::ClearShiledServerAdddress() { shiled_server_address_.clear(); }

void Primary::AddShiledServerAddress(const std::string& server_address) {
  shiled_server_address_.emplace_back(server_address);
}

void Primary::LaunchNewCommand() {
  if (context_->role != Role::kLeader) {
    return;
  }
  NoticeClientTask(kNewCommand);
}

void Primary::AddTask(TaskType type, bool is_delay) {
  switch (type) {
    case kHeartBeat: {
      if (is_delay) {
        uint64_t timeout = options_.heart_beat_us;
        double run_timeout = timeout / 1000000;
        loop_->RunAfter(run_timeout, false,
                        std::bind(&Primary::LaunchHeartBeat, this));
      } else {
        loop_->RunInLoop(std::bind(&Primary::LaunchHeartBeat, this));
      }
      break;
    }

    case kCheckLeader: {
      if (is_delay) {
        uint64_t timeout = options_.check_leader_us; 
        double run_timeout = timeout / 1000000;
        loop_->RunAfter(run_timeout, false,
                        std::bind(&Primary::LaunchCheckLeader, this));
      } else {
        loop_->RunInLoop(std::bind(&Primary::LaunchCheckLeader, this));
      }
      break;
    }

    case kNewCommand: {
      loop_->RunInLoop(std::bind(&Primary::LaunchNewCommand, this));
      break;
    }
  }
}

}  // namespace libraft